/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2024年7月4日
 * V4.0
 */
package com.jphenix.service.db.util;

import com.jphenix.standard.docs.ClassInfo;

/**
 * 雪花算法生成Long型ID值
 * com.jphenix.service.db.util.SnowflakeID
 * 
 * 注意：该类需要常驻内存
 * 
 * @author MBG
 * 2024年7月4日
 */
@ClassInfo({"2024-07-04 10:39","雪花算法生成Long型ID值"})
public class SnowflakeID {

	//#region 【变量声明区】
    private long datacenterId; // 数据中心ID
    private long machineId;    // 机器标识ID
    private long sequence;     // 序列号
    private long lastStamp;    // 上一次时间戳

    // 雪花算法参数
    private final long datacenterIdBits = 5L;                              // 数据中心ID所占位数
    private final long machineIdBits    = 5L;                              // 机器标识ID所占位数
    private final long sequenceBits     = 12L;                             // 序列号所占位数
    private final long maxDatacenterId  = -1L ^ (-1L << datacenterIdBits); // 最大数据中心ID
    private final long maxMachineId     = -1L ^ (-1L << machineIdBits);    // 最大机器标识ID
    private final long sequenceMask     = -1L ^ (-1L << sequenceBits);     // 序列号掩码
    private final long epoch            = 1661932800000L;                  // 这里指定了一个固定的起始时间戳(从2015-01-01 00:00:00开始)
    //#endregion
    
    //#region SnowflakeID(datacenterId,machineId) 构造函数
    /**
     * 构造函数
     * @author MBG
     */
    public SnowflakeID(String datacenterName, String machineName) {
        if(datacenterName==null || datacenterName.length()<1) {
        	datacenterId = 0L;
        }else {
        	datacenterId = str2long(datacenterName);
        	if(datacenterId>maxDatacenterId) {
        		datacenterId = 0L;
        	}
        }
        if(machineName==null || machineName.length()<1) {
        	machineId    = 0L;
        }else {
        	machineId    = str2long(machineName);
        	if(machineId>maxMachineId) {
        		machineId = 0L;
        	}
        }
        this.sequence         = 0L;
        this.lastStamp        = -1L;
    }
    //#endregion
    
    //#region 【入口】 nextId() 获取下一个id值
    /**
     * 获取下一个id值
     * @return 下一个id值
     * 2024年7月4日
     * @author MBG
     */
    public synchronized long nextId() {
        long currStamp = System.currentTimeMillis(); // 获取现在的时间戳
        if (currStamp < lastStamp) { // 如果当前时间小于上一次时间戳，则抛出异常
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }
        if (currStamp == lastStamp) { // 如果当前时间等于上一次时间戳，则序列号+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) { // 如果序列号达到最大值，则等待下一个时间戳
                currStamp = getNextMillis();
            }
        } else { // 如果当前时间大于上一次时间戳，则序列号被重置
            sequence = 0L;
        }
        lastStamp = currStamp; // 保存当前时间戳

        // 生成ID，ID共64位，其中41位为时间戳，5位为数据中心ID，5位为机器标识ID，12位为序列号
        return (currStamp - epoch) << (datacenterIdBits + machineIdBits + sequenceBits)
                | (datacenterId << (machineIdBits + sequenceBits))
                | (machineId << sequenceBits)
                | sequence;
    }
    //#endregion
    
    //#region 【内部方法】获取下一个毫秒值
    /**
     * 获取下一个毫秒值
     * @param lastStamp 上一个毫秒值
     * @return 
     * 2024年7月4日
     * @author MBG
     */
    private long getNextMillis() {
        long currMillis = System.currentTimeMillis(); // 获取现在的时间戳
        while (currMillis <= lastStamp) { // 如果当前时间小于等于上一次时间戳，则一直循环等待新的时间戳
            currMillis = System.currentTimeMillis();
        }
        return currMillis;
    }
    //#endregion
    
    //#region 【内部方法】 str2long(str) 字符串转为long型，确保不同的字符串long值也不同
    /**
     * 字符串转为long型，确保不同的字符串long值也不同
     * @param str   待转换的字符串
     * @return      转换后的值
     * 2024年7月4日
     * @author MBG
     */
    private static long str2long(String str) {
    	// 将字符串转为字节数组
    	byte[] bytes = str.getBytes();
    	long reLong = 0;
    	for(int i=0;i<bytes.length;i++) {
    		reLong += (long)bytes[i] * (bytes.length-i) * 16;
    	}
    	return reLong;
    }
    //#endregion
}








